package com.mincko.minckotest.server;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.google.appengine.api.blobstore.BlobInfo;
import com.google.appengine.api.blobstore.BlobInfoFactory;
import com.google.appengine.api.blobstore.BlobKey;
import com.google.appengine.api.blobstore.BlobstoreService;
import com.google.appengine.api.blobstore.BlobstoreServiceFactory;
import com.google.appengine.api.files.AppEngineFile;
import com.google.appengine.api.files.FileService;
import com.google.appengine.api.files.FileServiceFactory;
import com.google.appengine.api.files.FileWriteChannel;
import com.google.appengine.api.images.Composite;
import com.google.appengine.api.images.Image;
import com.google.appengine.api.images.ImagesService.OutputEncoding;
import com.google.appengine.api.images.ImagesServiceFactory;
import com.google.appengine.api.images.ImagesService;
import com.google.appengine.api.images.Transform;
import com.google.gson.Gson;

public class Upload extends HttpServlet {
	private BlobstoreService blobstoreService = BlobstoreServiceFactory
			.getBlobstoreService();
	private Integer targetWidth = 200;
	private Integer targetHeight = 200;

	private String status = ""; // to indicate to the client wether the image is
								// successful or not. 0=success, 1=Step1,
								// 2=Step2, 3=Step3

	public void doPost(HttpServletRequest req, HttpServletResponse res)
			throws ServletException, IOException {

		Map<String, BlobKey> blobs = blobstoreService.getUploadedBlobs(req);

		BlobKey blobKey = blobs.get("photoimg");
		String width = req.getParameter("width");
		String height = req.getParameter("height");
		String jsonRet = null;

		if (width == null) { // first time
			BlobInfo info = new BlobInfoFactory().loadBlobInfo(blobKey);

			ImagesService imagesService = ImagesServiceFactory
					.getImagesService();

			// Try to fetch image data
			byte[] bytes;
			try {
				bytes = blobstoreService.fetchData(blobKey, 0, info.getSize());
			}
			catch ( Exception e){
				// If not possible, first resize to acceptable size
				Image bigImage = ImagesServiceFactory.makeImageFromBlob(blobKey);
				Transform initialResize = ImagesServiceFactory.makeResize(1024, 4000);
				Image resizedImage = imagesService.applyTransform(initialResize, bigImage);
				bytes = resizedImage.getImageData();
			}
			
			// We now have an acceptable image for further processing	
			Image oldImage = ImagesServiceFactory.makeImage(bytes);

			Integer intWidth = oldImage.getWidth();
			Integer intHeight = oldImage.getHeight();

			// step 1: create new image with white background and center image
			// in middle; return;
			// create new image with white background and center image in
			// middle; return;
			// trying composite
			if (intWidth < targetWidth && intHeight < targetHeight) {
				status = "1";
				blobKey = getBlobKeyImage(
						blobKey,
						(setImageMiddle(intWidth, intHeight, oldImage,
								imagesService)).getImageData());
				// preparing returnJSON
				String[] json = { blobKey.getKeyString(), status };
				jsonRet = new Gson().toJson(json);

			}
			// step 2: rescale image to targetWidth
			else if (intWidth > targetWidth) {
				// if ( true) {
				Transform resize = ImagesServiceFactory.makeResize(targetWidth,
						4000);

				Image newImage = imagesService.applyTransform(resize, oldImage);
				// getting the new height
				intHeight = newImage.getHeight();
				intWidth = newImage.getWidth();
				if (intHeight > targetHeight) { // step3: cropping the image
					status = "3";
					blobKey = getBlobKeyImage(blobKey, newImage.getImageData());
					String[] json = { blobKey.getKeyString(), status,
							targetWidth.toString(), targetHeight.toString(),
							intWidth.toString(), intHeight.toString() };
					jsonRet = new Gson().toJson(json);
				} else { // make the image middle
					status = "1";
					blobKey = getBlobKeyImage(
							blobKey,
							(setImageMiddle(intWidth, intHeight, oldImage,
									imagesService)).getImageData());
					String[] json = { blobKey.getKeyString(), status };
					jsonRet = new Gson().toJson(json);
				}

			} else if (intHeight > targetHeight) { // step3: cropping the image
				status = "3";
				String[] json = { blobKey.getKeyString(), status,
						targetWidth.toString(), targetHeight.toString(),
						intWidth.toString(), intHeight.toString() };
				jsonRet = new Gson().toJson(json);
			} else { // show the image, good, just show the image
				status = "0";
				String[] json = { blobKey.getKeyString(), status };
				jsonRet = new Gson().toJson(json);
			}
		}

		// return to client
		res.setContentType("application/json");
		res.setCharacterEncoding("utf-8");
		res.getWriter().write(jsonRet);

	}

	/* set image to middle with white bg */
	private Image setImageMiddle(Integer intWidth, Integer intHeight,
			Image oldImage, ImagesService imagesService) {
		List composites = new ArrayList();
		// bg image, might not required since the background already white.
		// Image bgImage =
		// ImagesServiceFactory.makeImage(getImageBytes("/WEB-INF/images/background.png"));
		// Composite c = ImagesServiceFactory.makeComposite(bgImage,0,0,(float)
		// 1.0, Composite.Anchor.TOP_LEFT);
		// composites.add(c);

		// image in middle
		int offsetWidth = 0;
		int offsetHeight = 0;

		if (targetWidth - intWidth > 0)
			offsetWidth = (targetWidth - intWidth) / 2;

		if (targetHeight - intHeight > 0)
			offsetHeight = (targetHeight - intHeight) / 2;

		Composite c2 = ImagesServiceFactory.makeComposite(oldImage,
				offsetWidth, offsetHeight, (float) 1.0,
				Composite.Anchor.TOP_LEFT);
		composites.add(c2);
		return (imagesService.composite(composites, targetWidth, targetHeight,
				0, OutputEncoding.PNG));
	}

	/* to store and return the blobkey */
	private BlobKey getBlobKeyImage(BlobKey blobKey, byte[] data)
			throws IOException {
		FileService fileService = FileServiceFactory.getFileService();
		AppEngineFile file = fileService.createNewBlobFile("image/jpg");

		FileWriteChannel writeChannel = fileService
				.openWriteChannel(file, true);
		// This time we write to the channel directly
		writeChannel.write(ByteBuffer.wrap(data));
		// Now finalize
		writeChannel.closeFinally();
		if (blobKey != null)
			try {
				deleteBlobKey(blobKey);
			} catch (IOException e) {
				// some error in deleting blob, google app issue
			}
		// Now read from the file using the Blobstore API
		return (BlobKey) fileService.getBlobKey(file);
	}

	private byte[] getImageBytes(String imagePath) throws IOException {
		ServletContext context = getServletContext();
		InputStream is = context.getResourceAsStream(imagePath);

		final File file = new File(context.getRealPath(imagePath));
		long length = file.length();

		byte[] bytes = new byte[(int) length];

		// Read in the bytes
		int offset = 0;
		int numRead = 0;
		while (offset < bytes.length
				&& (numRead = is.read(bytes, offset, bytes.length - offset)) >= 0) {
			offset += numRead;
		}

		// Ensure all the bytes have been read in
		if (offset < bytes.length) {
			throw new IOException("Could not completely read file "
					+ file.getName());
		}

		// Close the input stream and return bytes
		is.close();
		return bytes;
	}

	private void deleteBlobKey(BlobKey blobKey) throws IOException {
		// lbe: uncommented this for the time being due to errors.
		// blobstoreService.delete(blobKey);
	}
}